//-------------------------------------------------------------------
// Filename:     hal_digio.c
// Description:  HAL digital IO functionality
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// INCLUDES
//-------------------------------------------------------------------
#include "hal_defs.h"
#include "hal_int.h"
#include "hal_digio.h"

//-------------------------------------------------------------------
// LOCAL VARIABLES
//-------------------------------------------------------------------

static ISR_FUNC_PTR port0_isr_tbl[8] = 
{
    0
};
static ISR_FUNC_PTR port1_isr_tbl[8] = 
{
    0
};
static ISR_FUNC_PTR port2_isr_tbl[5] = 
{
    0
};


// Find a better place for this!
#define PICTL_PADSC_BM 0x80
#define PICTL_P2IEN_BM 0x40
#define PICTL_P0IENH_BM 0x20
#define PICTL_P0IENL_BM 0x10
#define PICTL_P2ICON_BM 0x08
#define PICTL_P1ICONH_BM 0x04
#define PICTL_P1ICONL_BM 0x02
#define PICTL_P1ICON_BM 0x06 // Combined - for compatibility
#define PICTL_P0ICON_BM 0x01

//-------------------------------------------------------------------
// GLOBAL FUNCTIONS
//-------------------------------------------------------------------
//-------------------------------------------------------------------
// @fn      halDigioConfig
// @brief   Configure the pin as specified by p.
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------

//comment: find better way of doing this. SFR registers can not be accessed by pointers, therefore code is different from MSP430 code
// comment: currently support only P0 and P1
uint8 halDigioConfig(const digioConfig *p)
{
    //  register volatile uint8* dir;
    register const uint8 bitmask = p->pin_bm;

    // Sanity check
    if ((bitmask == 0) || (bitmask != (uint8)BV(p->pin)))
    {
        return (HAL_DIGIO_ERROR);
    }

    switch (p->port)
    {
        case 0:
            P0SEL &= ~bitmask;
            if (p->dir == HAL_DIGIO_OUTPUT)
            {
                if (p->initval == 1)
                {
                    P0 |= bitmask;
                }
                else
                {
                    P0 &= ~bitmask;
                }
                P0DIR |= bitmask;
            }
            else
            // input
            {
                P0DIR &= ~bitmask;
            }
            break;
        case 1:
            P1SEL &= ~bitmask;
            if (p->dir == HAL_DIGIO_OUTPUT)
            {
                if (p->initval == 1)
                {
                    P1 |= bitmask;
                }
                else
                {
                    P1 &= ~bitmask;
                }
                P1DIR |= bitmask;
            }
            else
            // input
            {
                P1DIR &= ~bitmask;
            }
            break;
        case 2:
            P2SEL &= ~bitmask;
            if (p->dir == HAL_DIGIO_OUTPUT)
            {
                if (p->initval == 1)
                {
                    P2 |= bitmask;
                }
                else
                {
                    P2 &= ~bitmask;
                }
                P2DIR |= bitmask;
            }
            else
            // input
            {
                P2DIR &= ~bitmask;
            }
            break;
            //case 1: P1SEL &= ~bitmask; out = &P1OUT; dir = &P1DIR; break;
            //case 2: P2SEL &= ~bitmask; out = &P2OUT; dir = &P2DIR; break;
        default:
            return (HAL_DIGIO_ERROR);
    }
    return (HAL_DIGIO_OK);
}


//-------------------------------------------------------------------
// @fn      halDigioSet
// @brief   Set output pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioSet(const digioConfig *p)
{
    if (p->dir == HAL_DIGIO_OUTPUT)
    {
        switch (p->port)
        {
            case 0:
                P0 |= p->pin_bm;
                break;
            case 1:
                P1 |= p->pin_bm;
                break;
            case 2:
                P2 |= p->pin_bm;
                break;
            default:
                return (HAL_DIGIO_ERROR);
        }
        return (HAL_DIGIO_OK);
    }
    return (HAL_DIGIO_ERROR);
}


//-------------------------------------------------------------------
// @fn      halDigioClear
// @brief   Clear output pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioClear(const digioConfig *p)
{
    if (p->dir == HAL_DIGIO_OUTPUT)
    {
        switch (p->port)
        {
            case 0:
                P0 &= ~p->pin_bm;
                break;
            case 1:
                P1 &= ~p->pin_bm;
                break;
            case 2:
                P2 &= ~p->pin_bm;
                break;
            default:
                return (HAL_DIGIO_ERROR);
        }
        return (HAL_DIGIO_OK);
    }
    return (HAL_DIGIO_ERROR);
}

//-------------------------------------------------------------------
// @fn      halDigioToggle
// @brief   Toggle output pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioToggle(const digioConfig *p)
{
    if (p->dir == HAL_DIGIO_OUTPUT)
    {
        switch (p->port)
        {
            case 0:
                P0 ^= p->pin_bm;
                break;
            case 1:
                P1 ^= p->pin_bm;
                break;
            case 2:
                P2 ^= p->pin_bm;
                break;
            default:
                return (HAL_DIGIO_ERROR);
        }
        return (HAL_DIGIO_OK);
    }
    return (HAL_DIGIO_ERROR);
}

//-------------------------------------------------------------------
// @fn      halDigioGet
// @brief   Get value on input pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioGet(const digioConfig *p)
{
    if (p->dir == HAL_DIGIO_INPUT)
    {
        switch (p->port)
        {
            case 0:
                return (P0 &p->pin_bm ? 1 : 0);
            case 1:
                return (P1 &p->pin_bm ? 1 : 0);
            case 2:
                return (P2 &p->pin_bm ? 1 : 0);
            default:
                break;
        }
    }
    return (HAL_DIGIO_ERROR);
}

//-------------------------------------------------------------------
// @fn      halDigioIntConnect
// @brief   Connect function to IO interrupt
// @param   digioConfig* p - pointer to configuration structure for IO pin
//          ISR_FUNC_PTR func - pointer to function
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioIntConnect(const digioConfig *p, ISR_FUNC_PTR func)
{
    istate_t key;
    HAL_INT_LOCK(key);
    switch (p->port)
    {
        case 0:
            port0_isr_tbl[p->pin] = func;
            break;
        case 1:
            port1_isr_tbl[p->pin] = func;
            break;
        case 2:
            port2_isr_tbl[p->pin] = func;
            break;
        default:
            HAL_INT_UNLOCK(key);
            return (HAL_DIGIO_ERROR);
    }
    halDigioIntClear(p);
    HAL_INT_UNLOCK(key);
    return (HAL_DIGIO_OK);
}


//-------------------------------------------------------------------
// @fn      halDigioIntEnable
// @brief   Enable interrupt on IO pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioIntEnable(const digioConfig *p)
{
    switch (p->port)
    {
        case 0:
            P0IE = 1; // set P0IE bit
            P0IEN |= p->pin_bm;
            break;
        case 1:
            IEN2 |= 0x10; // set P1IE bit
            P1IEN |= p->pin_bm;
            break;
        case 2:
            IEN2 |= 0x02; // set P2IE bit
            P2IEN |= p->pin_bm;
            break;
        default:
            return (HAL_DIGIO_ERROR);
    }
    return (HAL_DIGIO_OK);
}

//-------------------------------------------------------------------
// @fn      halDigioIntDisable
// @brief   Disable interrupt on IO pin
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioIntDisable(const digioConfig *p)
{
    switch (p->port)
    {
        case 0:
            P0IEN &= ~p->pin_bm;
            break;
        case 1:
            P1IEN &= ~p->pin_bm;
            break;
        case 2:
            P2IEN &= ~p->pin_bm;
            break;
        default:
            return (HAL_DIGIO_ERROR);
    }
    return (HAL_DIGIO_OK);
}

//-------------------------------------------------------------------
// @fn      halDigioIntClear
// @brief   Clear interrupt flag
// @param   digioConfig* p - pointer to configuration structure for IO pin
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
uint8 halDigioIntClear(const digioConfig *p)
{
    switch (p->port)
    {
        case 0:
            P0IFG &= ~p->pin_bm;
            break;
        case 1:
            P1IFG &= ~p->pin_bm;
            break;
        case 2:
            P2IFG &= ~p->pin_bm;
            break;
        default:
            return (HAL_DIGIO_ERROR);
    }
    return (HAL_DIGIO_OK);
}

//-------------------------------------------------------------------
// @fn      halDigioIntSetEdge
// @brief   Set edge for IO interrupt
// @param   digioConfig* p - pointer to configuration structure for IO pin
//          edge - HAL_DIGIO_INT_FALLING_EDGE or HAL_DIGIO_INT_RISING_EDGE
// @return  uint8 - HAL_DIGIO_ERROR or HAL_DIGIO_OK
//-------------------------------------------------------------------
// Comment: all pins on port are configured at same time
uint8 halDigioIntSetEdge(const digioConfig *p, uint8 edge)
{
    switch (edge)
    {
        case HAL_DIGIO_INT_FALLING_EDGE:
            switch (p->port)
            {
            case 0:
                PICTL |= PICTL_P0ICON_BM; // set P0ICON high
                break;
            case 1:
                PICTL |= PICTL_P1ICON_BM; // set P1ICON high
                break;
            case 2:
                PICTL |= PICTL_P2ICON_BM; // set P2ICON high
                break;
            default:
                return (HAL_DIGIO_ERROR);
            }
            break;

        case HAL_DIGIO_INT_RISING_EDGE:
            switch (p->port)
            {
            case 0:
                PICTL &= ~PICTL_P0ICON_BM; // set P0ICON low
                break;
            case 1:
                PICTL &= ~PICTL_P1ICON_BM; // set P0ICON low
                break;
            case 2:
                PICTL &= ~PICTL_P2ICON_BM; // set P0ICON low
                break;
            default:
                return (HAL_DIGIO_ERROR);
            }
            break;

        default:
            return (HAL_DIGIO_ERROR);
    }
    return (HAL_DIGIO_OK);
}

//-------------------------------------------------------------------
// @fn      port0_ISR
// @brief   ISR framework for P0 digio interrupt
// @param   none
// @return  none
//-------------------------------------------------------------------
HAL_ISR_FUNCTION(port0_ISR, P0INT_VECTOR)
{
    register uint8 i;
    P0IF = 0;
    if (P0IFG)
    {
        for (i = 0; i < 8; i++)
        {
            register const uint8 pinmask = 1 << i;
            if (P0IFG &pinmask)
            {
                if (port0_isr_tbl[i] != 0)
                {
                    (*port0_isr_tbl[i])();
                }
                P0IFG &= ~pinmask;
            }
        }
        //__low_power_mode_off_on_exit();
    }
}

//-------------------------------------------------------------------
// @fn      port1_ISR
// @brief   ISR framework for P0 digio interrupt
// @param   none
// @return  none
//-------------------------------------------------------------------
HAL_ISR_FUNCTION(port1_ISR, P1INT_VECTOR)
{
    register uint8 i;
    P1IF = 0;
    if (P1IFG)
    {
        for (i = 0; i < 8; i++)
        {
            register const uint8 pinmask = 1 << i;
            if (P1IFG &pinmask)
            {
                if (port1_isr_tbl[i] != 0)
                {
                    (*port1_isr_tbl[i])();
                }
                P1IFG &= ~pinmask;
            }
        }
        //__low_power_mode_off_on_exit();
    }
}

//-------------------------------------------------------------------
// @fn      port2_ISR
// @brief   ISR framework for P2 digio interrupt
// @param   none
// @return  none
//-------------------------------------------------------------------
HAL_ISR_FUNCTION(port2_ISR, P2INT_VECTOR)
{
    register uint8 i;
    P2IF = 0;
    if (P2IFG)
    {
        for (i = 0; i < 5; i++)
        {
            register const uint8 pinmask = 1 << i;
            if (P2IFG &pinmask)
            {
                if (port2_isr_tbl[i] != 0)
                {
                    (*port2_isr_tbl[i])();
                }
                P2IFG &= ~pinmask;
            }
        }
        //__low_power_mode_off_on_exit();
    }
}
